home *** CD-ROM | disk | FTP | other *** search
- /* this file contains all the routines related to the speech capabilities of
- WannaSpeech.
- Written by: Guillermo A. Ortiz AppleSoft Developer Technical Support
- Date: 08/04/93
-
- 08/18/93 -
- */
- #include "App.h" /* Get the application includes/typedefs, etc. */
- #include "App.defs.h" /* Get various application definitions. */
- #include "App.protos.h" /* Get the prototypes for application. */
- #include "WannaSpeech.h" /* Get speech stuff trying to minimize files changed */
-
- Boolean gCanTalk;
-
- /* variables used in calls to set voice channel parameters */
- Fixed speechRate, pitchValue;
- OSType textMode;
-
- /* returns an instance of the voice picker component if it is available.
- it is used in the menu fixing routine as a boolean to test the existance
- of the component and also to prepare for presenting the dialog.
- */
- ComponentInstance OpenStdTTS(void)
- {
- Component ttsComponent;
- ComponentInstance ttsCInstance;
- ComponentDescription ttsDesc;
- short resID, oldResID;
- Handle ttsRH;
- Point where = {0,0};
- OSErr err = componentDontRegister;
-
- ttsDesc.componentType = 'stts';
- ttsDesc.componentSubType = 0;
- ttsDesc.componentManufacturer = 'appl';
- ttsDesc.componentFlags = 0;
- ttsDesc.componentFlagsMask = 0;
-
- if ( gCanTalk ) {
- if ((ttsComponent = FindNextComponent(nil, &ttsDesc)) )
- return ttsCInstance = OpenComponent(ttsComponent);
- }
- }
-
- /* When the user choses Select Voice this routine invokes the Voice Picker
- and changes the voice info struct in the document if necessary.
- */
- void SetVoice(FileRecHndl frHndl)
- {
- ComponentInstance voicePicker;
- StdTTSParams speechParams; /* struct used when calling standard TTS component */
- WindowPtr w = FrontWindow();
- short result = 1;
- ControlHandle ctl;
- Str255 pstr;
- TEHandle te1;
-
- if (voicePicker = OpenStdTTS() ) {
- speechParams = (*frHndl)->d.doc.docSpeech;
- if ( StdSpeechDialog(voicePicker, &speechParams, nil) ) { /* user changed the voice */
- /* allophones are supported by galatea if the new voice is not galatean then
- warn the user
- */
- if ( speechParams.curVoice.voice.creator != 'gala' ) {
- CNum2Ctl(w, kModeName, &ctl);
- CTEGetPStr(ctl, pstr);
- if (! pcmp(pstr, "\pAllophones")) {
- if ( result = HCenteredAlert(128,w, nil) != ok )
- goto bail;
-
- CNum2Ctl(w, kPhonemeCtl, &ctl);
- te1 = (TEHandle)GetCRefCon(ctl);
- if ((*te1)->hText) {
- TESetSelect(0, 32767, te1);
- TEDelete(te1);
- }
-
- SetTECtlText(w, frHndl, kModeName, "\pPhonemes");
- }
- }
- (*frHndl)->d.doc.docSpeech = speechParams;
- SetTECtlText(w, frHndl, kVoiceName, (*frHndl)->d.doc.docSpeech.curVoice.name);
- }
- bail:
- CloseComponent(voicePicker);
- }
- }
-
- /* This routine selects the input method for the channel depending on
- the text edit field that is active.
- */
- void DoSayIt(FileRecHndl frHndl)
- {
- WindowPtr w;
- ControlHandle ctl;
- TEHandle te1, te2;
- Str255 pstr;
-
- w = FrontWindow();
- CNum2Ctl(w, kTextCtl, &ctl);
- te1 = (TEHandle)GetCRefCon(ctl);
- te2 = CTEFindActive(nil);
- if (te1 == te2) {
- textMode = 'TEXT';
- }
- else { /* could be phonemes or allophones */
- textMode = 'PHON';
- CNum2Ctl(w, kModeName, &ctl);
- CTEGetPStr(ctl, pstr);
- if (! pcmp(pstr, "\pAllophones"))
- textMode = 'ALLO';
- }
- SayText(frHndl, te2);
- }
-
- /* pass nil if the window is active */
- void CreatePhonemesList(WindowPtr wind)
- {
- PhonemeDescriptor **phonemeSymbols;
- ControlHandle ctl;
- TEHandle te1;
- long phonemesLength;
- OSErr err;
- SpeechChannel sChannel;
- WindowPtr w;
- short i;
- short saveEnd;
- TextStyle ts;
- StdTTSParams speechParams;
-
- if (wind)
- w = wind;
- else
- w = FrontWindow();
- CNum2Ctl(w, kPhonemeLst, &ctl);
- te1 = (TEHandle)GetCRefCon(ctl);
-
- if (!(err = NewSpeechChannel(&speechParams.curVoice.voice, &sChannel)) ) { /* nil means give me the current channel */
-
- if ( ! (GetSpeechInfo(sChannel, soPhonemeSymbols, &phonemeSymbols) )) {
-
- if ((*te1)->hText) {
- TESetSelect(0, 32767, te1);
- TEDelete(te1);
- }
- for (i=0; i < (*phonemeSymbols)->phonemeCount; i++) {
- TEInsert(&((*phonemeSymbols)->thePhonemes[i].phStr[1]),
- (*phonemeSymbols)->thePhonemes[i].phStr[0], te1); /* insert phoneme */
- TEInsert(" ", 1, te1); /* space */
- TEInsert(&((*phonemeSymbols)->thePhonemes[i].exampleStr[1]),
- (*phonemeSymbols)->thePhonemes[i].exampleStr[0], te1); /* example */
- saveEnd = (*te1)->selEnd;
- (*te1)->selStart = saveEnd - (*phonemeSymbols)->thePhonemes[i].exampleStr[0] + (*phonemeSymbols)->thePhonemes[i].hiliteStart;
- (*te1)->selEnd = saveEnd - (*phonemeSymbols)->thePhonemes[i].exampleStr[0] + (*phonemeSymbols)->thePhonemes[i].hiliteEnd;
-
- ts.tsFace = bold;
- TESetStyle(doFace, &ts, false, te1);
- (*te1)->selStart = (*te1)->selEnd = saveEnd;
- // TESetSelect(saveEnd, saveEnd, te1);
- ts.tsFace = 0;
- TESetStyle(doFace, &ts, false, te1);
-
- TEInsert("\r", 1, te1); /* and return */
- }
- (*te1)->selStart = (*te1)->selEnd;
- TEDelete(te1);
-
- DisposeHandle((Handle)phonemeSymbols);
- TECalText(te1); /* Force all resizing to make tectl happy. */
- CTEAdjustTEBottom(te1);
- CTEAdjustScrollValues(te1);
- }
- err = DisposeSpeechChannel(sChannel);
- }
- }
-
- /* When it is necessary to change the in a TE control this is the code that does it.
- */
- void SetTECtlText(WindowPtr window, FileRecHndl frHndl, short ctlNumber, Str255 pstr)
- {
- ControlHandle ctl;
- TEHandle te1;
-
- CNum2Ctl(window, ctlNumber, &ctl);
- UseControlStyle(ctl);
- CTESetPStr(ctl, pstr);
- UseControlStyle(nil);
- }
-
- void DoMakePhonemes(FileRecHndl frHndl)
- {
- Handle phonemesBuffer;
- ControlHandle ctl;
- TEHandle te1, te2;
- long phonemesLength;
- OSErr err;
- SpeechChannel sChannel;
- WindowPtr w;
- CursHandle hWatchCursor;
-
- hWatchCursor = GetCursor(watchCursor);
- HLock((Handle)hWatchCursor);
-
- w = FrontWindow();
-
- CNum2Ctl(w, kTextCtl, &ctl);
- te1 = (TEHandle)GetCRefCon(ctl);
-
- CNum2Ctl(w, kPhonemeCtl, &ctl);
- te2 = (TEHandle)GetCRefCon(ctl);
-
- if (!( phonemesBuffer = NewHandleClear(((*te1) -> teLength)*2) )) {
- NewDocumentWindow(nil, 'ALRT', false);
- return;
- }
-
- HLock((Handle) te1);
- HLock((Handle) (*te1)->hText);
-
-
- SetCursor( *(hWatchCursor));
-
- if (!(err = NewSpeechChannel(&((*frHndl)->d.doc.docSpeech.curVoice.voice), &sChannel)) ) { /* nil means give me the current channel */
-
- if (err = TextToPhonemes(sChannel, *((*te1) -> hText), (*te1) -> teLength, phonemesBuffer, &phonemesLength) ) {
- DebugStr("\pTextToPhonemes failed");
- goto bail;
- }
-
- SetTECtlText(w, frHndl, kModeName, "\pPhonemes");
- SetHandleSize(phonemesBuffer, phonemesLength);
- phonemesBuffer = CTESwapText(te2, phonemesBuffer, nil, true);
-
- err = DisposeSpeechChannel(sChannel);
- }
- bail:
- InitCursor();
- HUnlock((Handle) (*te1)->hText);
- HUnlock((Handle) te1);
- DisposeHandle(phonemesBuffer);
-
- CTESetSelect(0, (*te2) -> teLength, te2);
- CTEActivate(true, te2);
- HUnlock((Handle)hWatchCursor);
-
- }
-
- void DoMakeAllophones(FileRecHndl frHndl) /* GALA voices only */
- {
- Handle allophonesBuffer;
- ControlHandle ctl;
- TEHandle te1, te2;
- long allophonesLength;
- OSErr err;
- SpeechChannel sChannel;
- WindowPtr w;
- CursHandle hWatchCursor;
-
- hWatchCursor = GetCursor(watchCursor);
- HLock((Handle)hWatchCursor);
-
-
- w = FrontWindow();
-
- CNum2Ctl(w, kTextCtl, &ctl);
- te1 = (TEHandle)GetCRefCon(ctl);
-
- CNum2Ctl(w, kPhonemeCtl, &ctl);
- te2 = (TEHandle)GetCRefCon(ctl);
-
- if (!( allophonesBuffer = NewHandleClear(((*te1) -> teLength)*2) )) {
- NewDocumentWindow(nil, 'ALRT', false);
- return;
- }
-
- HLock((Handle) te1);
- HLock((Handle) (*te1)->hText);
-
- SetCursor( *(hWatchCursor));
-
- if (!(err = NewSpeechChannel(&((*frHndl)->d.doc.docSpeech.curVoice.voice), &sChannel)) ) { /* nil means give me the current channel */
-
- if (err = TextToAllophones(sChannel, *((*te1) -> hText), (*te1) -> teLength, allophonesBuffer, &allophonesLength) ) {
- DebugStr("\pTextToallophones failed");
- goto bail;
- }
-
- InitCursor();
- SetTECtlText(w, frHndl, kModeName, "\pAllophones");
- SetHandleSize(allophonesBuffer, allophonesLength);
- allophonesBuffer = CTESwapText(te2, allophonesBuffer, nil, true);
-
- err = DisposeSpeechChannel(sChannel);
- }
- bail:
- InitCursor();
- HUnlock((Handle) (*te1)->hText);
- HUnlock((Handle) te1);
- DisposeHandle(allophonesBuffer);
-
- CTESetSelect(0, (*te2) -> teLength, te2);
- CTEActivate(true, te2);
- HUnlock((Handle)hWatchCursor);
- }
-
- /* this little routine uses Gestalt to check for the presence of
- the text to speech manager.
- */
- Boolean SpeechAvailable(void)
- {
- OSErr err;
- long result;
-
- err = Gestalt(gestaltSpeechAttr, &result);
- if ( (err) || (!(result &(1<<gestaltSpeechMgrPresent)))) {
- DebugStr("\pSpeech Manager is not available");
- return false;
- }
- else
- return true;
- }
-
- pascal void MyWordCallback(SpeechChannel sChannel, WordLimitsPtr wLP, long wordPos, short wordLen)
- /* We will use the word call back to hilite the next word to be spoken.
- The problem is that this proc is called at interrupt time and can not
- use something that moves memory, so here we only save the information and
- let the "while speechbusy loop do the actual hiliting.
- */
- {
- wLP -> hilite = true; /* flag that word needs hiliting */
- wLP -> wordStart = wordPos;
- wLP -> wordEnd = wordPos + wordLen;
- }
-
- OSErr SayText(FileRecHndl frHndl, TEHandle teH)
- /* - Calls SpeakText.
- - Opens a speech channel,
- - holds until done
- - hilites the word being spoken
- - checks for command period
- - disposes of the channel.
- */
- {
- SpeechChannel sChannel;
- KeyMap kMap;
- TEHandle oldActive;
- OSErr err = noErr; /* optimism abounds */
- WordLimits wLims;
- GTXtndData myData = {'gala','inpt', 'ALLO'};
- CursHandle hWatchCursor;
-
- hWatchCursor = GetCursor(watchCursor);
- HLock((Handle)hWatchCursor);
-
-
- if ( ! SpeechAvailable() ) return;
- HLock((Handle) teH);
- HLock((Handle) (*teH)->hText);
-
- SetCursor( *(hWatchCursor));
-
- if (!(err = NewSpeechChannel(&((*frHndl)->d.doc.docSpeech.curVoice.voice), &sChannel)) ) { /* nil means give me the current channel */
- SetSpeechInfo(sChannel, soRefCon, &wLims);
- SetSpeechInfo(sChannel, soWordCallBack, MyWordCallback); /* does not get called for phonemes */
-
- SetSpeechInfo(sChannel, 'rate', &((*frHndl)->d.doc.docSpeech.rate));
- SetSpeechInfo(sChannel, 'pmod', &((*frHndl)->d.doc.docSpeech.modulation));
- SetSpeechInfo(sChannel, 'pbas', &((*frHndl)->d.doc.docSpeech.pitch));
-
- if ( textMode == 'ALLO' ) // Allophones are set via the extended command mechanism, only galatea voices support this mode
- SetSpeechInfo(sChannel, 'xtnd', &myData);
- else
- SetSpeechInfo(sChannel, soInputMode, &textMode);
- /* need to activate to let user see the highlited words as they
- are spoken.
- */
-
- if (!(err = SpeakText(sChannel, *((*teH) -> hText), (*teH) -> teLength))) {
- while (SpeechBusy() > 0) { /* Need to wait until all stops */
-
- if ( wLims.hilite) { /* first we check if word being spoken needs hiliting */
- CTESetSelect(wLims.wordStart, wLims.wordEnd, teH);
- wLims.hilite = false; /* do it only once */
- }
- GetKeys(kMap);
- if ((kMap[1] == 0x808000) ){ /* user is tired of the long message */
- err = StopSpeech(sChannel);
- break;
- }
- }
- }
-
- InitCursor();
-
- SetSpeechInfo(sChannel, soWordCallBack, nil);
- err = DisposeSpeechChannel(sChannel);
- }
- HUnlock((Handle) (*teH)->hText); /* release handles */
- HUnlock((Handle) teH);
- HUnlock((Handle)hWatchCursor);
- return err;
- }
-
- void InitDocSpeech(FileRecHndl frHndl)
- {
- SpeechChannel sChannel;
- StdTTSParams speechParams;
-
- if (gCanTalk) { /* make sure we can talk */
- if ( GetVoiceDescription(nil, &speechParams.curVoice, sizeof(VoiceDescription)) )
- goto bail;
-
- if (NewSpeechChannel(&(speechParams.curVoice.voice), &sChannel) ) /* try voice in record */
- goto bail;
-
- GetSpeechInfo(sChannel, 'rate', &speechParams.rate);
- GetSpeechInfo(sChannel, 'pbas', &speechParams.pitch);
- GetSpeechInfo(sChannel, 'pmod', &speechParams.modulation);
- DisposeSpeechChannel(sChannel);
- (*frHndl)->d.doc.docSpeech = speechParams;
- (*frHndl)->d.doc.docSpeech.message = (char *)"\pPlease select a voice:";
- (*frHndl)->d.doc.docSpeech.flags = 0;
- return;
- }
- bail:
- NewDocumentWindow(nil, 'ALRT', false);
- (*frHndl)->d.doc.docSpeech.flags = -1;
- return;
- }
-
- pascal OSErr TextToAllophones(SpeechChannel channel, Ptr textBuf, long textBytes, Handle allophoneBuf, long *allophoneBytes)
- {
- OSErr err = paramErr; /* very optimistic */
- GTXtndConvertData convertAllo;
- GTXtndData myData;
-
- /* set up struct for GetSpeechInfo call */
- myData.synthID = soGalaSynthID;
- myData.selector = 'xtnd';
- myData.info2 = (long) &convertAllo;
-
- /* set up struct which calls and passes info to convert to allophones */
- convertAllo.synthID = soGalaSynthID;
- convertAllo.selector = soGalaConvertToAllo;
- convertAllo.inputBuffer = textBuf;
- convertAllo.inputLen = textBytes;
- convertAllo.controlFlags = 0;
- convertAllo.outputBuffer = allophoneBuf;
- convertAllo.outputLen = 0;
-
- if ( !(err = GetSpeechInfo(channel, 'xtnd', &convertAllo) )) {
- *allophoneBytes = convertAllo.outputLen;
- }
-
- return err;
- }
-
-